///
/// This is the main() for the Client
/// Alok Sood
///

#include <iostream>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cassert>
#include "common.h"
//#include <cstdio>

int mainFD;

int SendJoin(const char* userName)
{
	const unsigned int userNameLength = strlen(userName);

	StructAttribute attribute;
	attribute.type = ATTR_TYPE_USERNAME;
	attribute.length = userNameLength + ATTR_SIZE;
	
	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_JOIN;
	header.length = attribute.length + HEADER_SIZE;

	char msgBuf[MSG_BUF_SIZE];
	char* dest = &msgBuf[0];

	memcpy(dest, (const char*)&header, HEADER_SIZE);
	dest += HEADER_SIZE;

	memcpy(dest, (const char*)&attribute, ATTR_SIZE);
	dest += ATTR_SIZE;

	memcpy(dest, userName, userNameLength);
	dest += userNameLength;

	//sanity check
	assert((dest - msgBuf) == header.length);

/*	std::cout << "Sending " << header.length << " byte message to server: " << std::endl;
	for(unsigned int i = 0; i < header.length; ++i)
	{
		printf("%x ", msgBuf[i]);
	}
	std::cout << std::endl;*/

	return send(mainFD, msgBuf, header.length, 0);
}

int main(int argc, char** argv)
{

	//make sure you're called correctly
	if(argc != 4)
	{
		std::cout << "Usage: ./client serverIP serverPort username" << std::endl;
		return 1;
	}

	int retVal;

	//unsigned short portNo = atoi(argv[2]); //lets assume that the user is knowledgeable

	struct addrinfo temp, *res;

	memset(&temp, 0, sizeof(temp));		temp.ai_family = AF_UNSPEC;		temp.ai_socktype = SOCKET_TYPE;

	if((retVal = getaddrinfo(argv[1], argv[2], &temp, &res)) != 0)
	{
		std::cout << "getaddrinfo() error: " << gai_strerror(retVal) << std::endl;
		return 1;
	}

	//sanity check on username
	const char *userName = argv[3];
	if(strlen(userName) > 16)
	{
		std::cout << "Username can't be more than 16 characters long" << std::endl;
		return 1;
	}
	for(unsigned int i = 0; i < strlen(userName); ++i)
	{
		if(!(((userName[i] >= 'a') && (userName[i] <= 'z')) ||
			 ((userName[i] >= 'A') && (userName[i] <= 'Z')) || 
			 ((userName[i] >= '0') && (userName[i] <= '9')) ||
			 (userName[i] == '_')))
		{
			std::cout << "Username can contain an alphabet(either case), a number, or an underscore only" << std::endl;
			return 1;
		}
	}

	std::cout << "Connecting to server..." << std::endl;
	std::cout << "IP Address: " << argv[1] << std::endl;
	std::cout << "Port Number: " << argv[2] << std::endl;
	std::cout << "User Name: " << argv[3] << std::endl;


	//create client socket
	mainFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if(mainFD == -1)
	{
		perror("socket() error: ");
		return 2;
	}

	//TODO: see if you need to set reusability to this socket
	/*int yes = 1;
	if(setsockopt(mainFD, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	{
		perror("setsockopt() error: ");
		return 2;
	}*/

	if(connect(mainFD, res->ai_addr, res->ai_addrlen) == -1)
	{
		perror("connect() error: ");
		close(mainFD);
		return 2;
	}

	std::cout << "Connected successfully to server!" << std::endl;

	freeaddrinfo(res);

	/*char buf[1000];
	int readBytes;

	if((readBytes = recv(mainFD, buf, 999, 0)) == -1)
	{
		perror("recv() error: ");
		close(mainFD);
		return 3;
	}
	buf[readBytes] = 0;

	std::cout << "Server says: " << buf << std::endl;*/

	//initialize FD set for select()
	fd_set readFDS, connectedFDS;
	FD_ZERO(&readFDS);
	FD_ZERO(&connectedFDS);
	int maxFD;
	
	FD_SET(mainFD, &connectedFDS);
	FD_SET(STDIN, &connectedFDS); //for command line control of server
	maxFD = std::max(STDIN, mainFD);

	if(SendJoin(userName) == -1)
	{
		std::cout << "Couldn't send JOIN... exiting!" << std::endl;
		close(mainFD);
		return 2;
	}

	//TODO: receive ACK / NACK from server

	char msgBuf[MSG_BUF_SIZE];

	while(1)
	{
		readFDS = connectedFDS;
		if(select(maxFD+1, &readFDS, NULL, NULL, NULL) == -1)
		{
			perror("select() error: ");
			return 3;
		}

		for(int i = 0; i <= maxFD; ++i)
		{
			if(FD_ISSET(i, &readFDS))
			{
				if(i == STDIN)
				{
					//handle std input commands
					if(!fgets(msgBuf, MSG_BUF_SIZE, stdin))
					{
						if(ferror(stdin))
						{
							perror("fgets(stdin) error: ");
							close(mainFD);
							return 3;
						}
					}

					//std::cout << "Sending to server...";

					//TODO: SEND to server

					/*if(send(mainFD, msgBuf, strlen(msgBuf), 0) == -1)
					{
						perror("send() error: ");
						close(mainFD);
						return 3;
					}
					std::cout << "sent!" << std::endl;*/
				}
				else if(i == mainFD)
				{
					//TODO: handle request from server
					//on ACK, FWD, ONLINE, OFFLINE, display it on STDOUT
					//on NACK, display error on STDOUT and close socket and exit

					//HandleMessageFromServer();

					/*int byteCount = recv(mainFD, msgBuf, MSG_BUF_SIZE, 0);
					if(byteCount == -1)
					{
						perror("recv() error: ");
						close(mainFD);
						return 3;
					}
					msgBuf[byteCount] = 0;

					std::cout << "Server: " << msgBuf << std::endl;*/

				}
				else
				{
					assert(false);
				}
			}
		}
	}

	close(mainFD);

	return 0;
}
